/*
************************************************************************************************************************
* File    : cpu_port.c
* By      : xyou
* Version : V1.00.00
*
* By      : prife
* Version : V1.00.01
************************************************************************************************************************
*/

/*
*********************************************************************************************************
*                                             INCLUDE FILES
*********************************************************************************************************
*/
#include  <rtthread.h>
#include  <windows.h>
#include  <mmsystem.h>
#include  <stdio.h>
#include  "cpu_port.h"

/*
*********************************************************************************************************
*                                             WinThread STRUCTURE
*  Windows runs each task in a thread.
*  The context switch is managed by the threads.So the task stack does not have to be managed directly,
*  although the stack stack is still used to hold an WinThreadState structure this is the only thing it
*  will be ever hold.
*  the structure indirectly maps the task handle to a thread handle
*********************************************************************************************************
*/
typedef struct {
	void*            Param;                     //Thread param
	void (*Entry)(void*);                       //Thread entry
	void (*Exit)(void);                                 //Thread exit
	HANDLE          ThreadHandle;
	DWORD           ThreadID;
} win_thread_t;

const DWORD MS_VC_EXCEPTION = 0x406D1388;

#pragma pack(push,8)
typedef struct tagTHREADNAME_INFO {
	DWORD dwType; // Must be 0x1000.
	LPCSTR szName; // Pointer to name (in user addr space).
	DWORD dwThreadID; // Thread ID (-1=caller thread).
	DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;
#pragma pack(pop)

/*
*********************************************************************************************************
*                                             LOCAL DEFINES
*********************************************************************************************************
*/
#define MAX_INTERRUPT_NUM       ((rt_uint32_t)sizeof(rt_uint32_t) * 8)

/*
 * Simulated interrupt waiting to be processed.this is a bit mask where each bit represent one interrupt
 * so a maximum of 32 interrupts can be simulated
 */
static volatile rt_uint32_t  CpuPendingInterrupts = 0;

/*
 * An event used to inform the simulated interrupt processing thread (a high priority thread
 *      that simulated interrupt processing) that an interrupt is pending
 */
static HANDLE   hInterruptEventHandle = NULL;

/*
 * Mutex used to protect all the simulated interrupt variables that are accessed by multiple threads
 */
static HANDLE   hInterruptEventMutex = NULL;

/*
 * Handler for all the simulate software interrupts.
 * The first two positions are used the Yield and Tick interrupt so are handled slightly differently
 * all the other interrupts can be user defined
*/
static rt_uint32_t (*CpuIsrHandler[MAX_INTERRUPT_NUM])(void) = {0};

/*
 * Handler for OSTick Thread
 */
static HANDLE       OSTick_Thread;
static DWORD        OSTick_ThreadID;
static HANDLE       OSTick_SignalPtr;
static TIMECAPS     OSTick_TimerCap;
static MMRESULT     OSTick_TimerID;

/*
 * flag in interrupt handling
 */
rt_uint32_t rt_interrupt_from_thread, rt_interrupt_to_thread;
rt_uint32_t rt_thread_switch_interrupt_flag;

/*
*********************************************************************************************************
*                                             PRIVATE FUNCTION PROTOTYPES
*********************************************************************************************************
*/
//static void WinThreadScheduler(void);
void WinThreadScheduler(void);
rt_uint32_t YieldInterruptHandle(void);
rt_uint32_t SysTickInterruptHandle(void);
static DWORD WINAPI ThreadforSysTickTimer(LPVOID lpParam);
static DWORD WINAPI ThreadforKeyGet(LPVOID lpParam);

static void SetThreadName(DWORD dwThreadID, char* threadName)
{
#if defined(_MSC_VER)
	THREADNAME_INFO info;
	info.dwType = 0x1000;
	info.szName = threadName;
	info.dwThreadID = dwThreadID;
	info.dwFlags = 0;

	__try {
		RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
	} __except(EXCEPTION_EXECUTE_HANDLER) {
	}

#endif
}

/*
*********************************************************************************************************
*                                            rt_hw_stack_init()
* Description : Initialize stack of thread
* Argument(s) : void *pvEntry,void *pvParam,rt_uint8_t *pStackAddr,void *pvExit
* Return(s)   : rt_uint8_t*
* Caller(s)   : rt_thread_init or rt_thread_create
* Note(s)     : none
*********************************************************************************************************
*/

static DWORD WINAPI thread_run(LPVOID lpThreadParameter)
{
	rt_thread_t tid = rt_thread_self();
	win_thread_t*  pWinThread = (win_thread_t*)lpThreadParameter;

	SetThreadName(GetCurrentThreadId(), tid->name);

	pWinThread->Entry(pWinThread->Param);

	pWinThread->Exit();
	return 0;
}

rt_uint8_t* rt_hw_stack_init(void* pEntry, void* pParam, rt_uint8_t* pStackAddr, void* pExit)
{
	win_thread_t*    pWinThread = NULL;

	/*
	 * In this simulated case a stack is not initialized
	 * The thread handles the context switching itself. The WinThreadState object is placed onto the stack
	 * that was created for the task
	 * so the stack buffer is still used,just not in the conventional way.
	 */
	pWinThread = (win_thread_t*)(pStackAddr - sizeof(win_thread_t));

	pWinThread->Entry = pEntry;
	pWinThread->Param = pParam;
	pWinThread->Exit = pExit;

	pWinThread->ThreadHandle = NULL;
	pWinThread->ThreadID = 0;

	/* Create the winthread */
	pWinThread->ThreadHandle = CreateThread(NULL,
	                                        0,
	                                        (LPTHREAD_START_ROUTINE) thread_run,
	                                        pWinThread,
	                                        CREATE_SUSPENDED,
	                                        &(pWinThread->ThreadID));
	SetThreadAffinityMask(pWinThread->ThreadHandle,
	                      0x01);
	SetThreadPriorityBoost(pWinThread->ThreadHandle,
	                       TRUE);
	SetThreadPriority(pWinThread->ThreadHandle,
	                  THREAD_PRIORITY_IDLE);

	return (rt_uint8_t*)pWinThread;
} /*** rt_hw_stack_init ***/

/*
*********************************************************************************************************
*                                            rt_hw_interrupt_disable()
* Description : disable cpu interrupts
* Argument(s) : void
* Return(s)   : rt_base_t
* Caller(s)   : Applicatios or os_kernel
* Note(s)     : none
*********************************************************************************************************
*/
rt_base_t rt_hw_interrupt_disable(void)
{
	if(hInterruptEventMutex != NULL) {
		WaitForSingleObject(hInterruptEventMutex, INFINITE);
	}

	return 0;
} /*** rt_hw_interrupt_disable ***/


/*
*********************************************************************************************************
*                                            rt_hw_interrupt_enable()
* Description : enable cpu interrupts
* Argument(s) : rt_base_t level
* Return(s)   : void
* Caller(s)   : Applications or os_kernel
* Note(s)     : none
*********************************************************************************************************
*/
void rt_hw_interrupt_enable(rt_base_t level)
{
	level = level;

	if(hInterruptEventMutex != NULL) {
		ReleaseMutex(hInterruptEventMutex);
	}

} /*** rt_hw_interrupt_enable ***/

/*
*********************************************************************************************************
*                                            rt_hw_context_switch_interrupt()
* Description : switch thread's contex
* Argument(s) : void
* Return(s)   : void
* Caller(s)   : os kernel
* Note(s)     : none
*********************************************************************************************************
*/
void rt_hw_context_switch_interrupt(rt_uint32_t from,
                                    rt_uint32_t to)
{
	if(rt_thread_switch_interrupt_flag != 1) {
		rt_thread_switch_interrupt_flag = 1;

		// set rt_interrupt_from_thread
		rt_interrupt_from_thread = *((rt_uint32_t*)(from));
	}

	rt_interrupt_to_thread = *((rt_uint32_t*)(to));

	//trigger YIELD exception(cause context switch)
	TriggerSimulateInterrupt(CPU_INTERRUPT_YIELD);
} /*** rt_hw_context_switch_interrupt ***/



void rt_hw_context_switch(rt_uint32_t from,
                          rt_uint32_t to)
{
	if(rt_thread_switch_interrupt_flag != 1) {
		rt_thread_switch_interrupt_flag  = 1;

		// set rt_interrupt_from_thread
		rt_interrupt_from_thread = *((rt_uint32_t*)(from));

	}

	// set rt_interrupt_to_thread
	rt_interrupt_to_thread = *((rt_uint32_t*)(to));

	//trigger YIELD exception(cause contex switch)
	TriggerSimulateInterrupt(CPU_INTERRUPT_YIELD);

} /*** rt_hw_context_switch ***/

/*
*********************************************************************************************************
*                                            rt_hw_context_switch_to()
* Description : switch to new thread
* Argument(s) : rt_uint32_t to              //the stack address of the thread which will switch to
* Return(s)   : void
* Caller(s)   : rt_thread schecale
* Note(s)     : this function is used to perform the first thread switch
*********************************************************************************************************
*/
void rt_hw_context_switch_to(rt_uint32_t to)
{
	//set to thread
	rt_interrupt_to_thread = *((rt_uint32_t*)(to));

	//clear from thread
	rt_interrupt_from_thread = 0;

	//set interrupt to 1
	rt_thread_switch_interrupt_flag = 1;

	//start WinThreadScheduler
	WinThreadScheduler();

	//never reach here!
	return;

} /*** rt_hw_context_switch_to ***/



/*
*********************************************************************************************************
*                                            TriggerSimulateInterrupt()
* Description : Trigger a simulated interrupts handle
* Argument(s) : t_uint32_t IntIndex
* Return(s)   : void
* Caller(s)   : Applications
* Note(s)     : none
*********************************************************************************************************
*/
void TriggerSimulateInterrupt(rt_uint32_t IntIndex)
{
	if((IntIndex < MAX_INTERRUPT_NUM) && (hInterruptEventMutex != NULL)) {
		/* Yield interrupts are processed even when critical nesting is non-zero  */
		WaitForSingleObject(hInterruptEventMutex,
		                    INFINITE);

		CpuPendingInterrupts |= (1 << IntIndex);

		SetEvent(hInterruptEventHandle);

		ReleaseMutex(hInterruptEventMutex);
	}
} /*** TriggerSimulateInterrupt ***/

/*
*********************************************************************************************************
*                                            RegisterSimulateInterrupt()
* Description : Register a interrupt handle to simulate paltform
* Argument(s) : rt_uint32_t IntIndex,rt_uint32_t (*IntHandler)(void)
* Return(s)   : void
* Caller(s)   : Applications
* Note(s)     : none
*********************************************************************************************************
*/
void RegisterSimulateInterrupt(rt_uint32_t IntIndex, rt_uint32_t (*IntHandler)(void))
{
	if(IntIndex < MAX_INTERRUPT_NUM) {
		if(hInterruptEventMutex != NULL) {
			WaitForSingleObject(hInterruptEventMutex,
			                    INFINITE);

			CpuIsrHandler[IntIndex] = IntHandler;

			ReleaseMutex(hInterruptEventMutex);
		} else {
			CpuIsrHandler[IntIndex] = IntHandler;
		}
	}

} /*** RegisterSimulateInterrupt ***/



/*
*********************************************************************************************************
*                                             PRIVATE FUNCTION
*********************************************************************************************************
*/

/*
*********************************************************************************************************
*                                            WinThreadScheduler()
* Description : Handle all simulate interrupts
* Argument(s) : void
* Return(s)   : static void
* Caller(s)   : os scachle
* Note(s)     : none
*********************************************************************************************************
*/
#define WIN_WM_MIN_RES      (1)
void WinThreadScheduler(void)
{
	HANDLE          hInterruptObjectList[2];
	HANDLE          hThreadHandle;
	rt_uint32_t     SwitchRequiredMask;
	rt_uint32_t     i;

	win_thread_t*    WinThreadFrom;
	win_thread_t*    WinThreadTo;

	/*
	 * Install the interrupt handlers used bye scheduler itself
	 */
	RegisterSimulateInterrupt(CPU_INTERRUPT_YIELD,
	                          YieldInterruptHandle);
	RegisterSimulateInterrupt(CPU_INTERRUPT_TICK,
	                          SysTickInterruptHandle);

	/*
	 * Create the events and mutex that are used to synchronise all the WinThreads
	 */
	hInterruptEventMutex = CreateMutex(NULL,
	                                   FALSE,
	                                   NULL);
	hInterruptEventHandle = CreateEvent(NULL,
	                                    FALSE,
	                                    FALSE,
	                                    NULL);

	if((hInterruptEventMutex == NULL) || (hInterruptEventHandle == NULL)) {
		return;
	}

	/*
	 * Set the priority of this WinThread such that it is above the priority of the WinThreads
	 * that run rt-threads.
	 * This is higher priority is required to ensure simulate interrupts take priority over rt-threads
	 */
	hThreadHandle = GetCurrentThread();

	if(hThreadHandle == NULL) {
		return;
	}

	if(SetThreadPriority(hThreadHandle,
	                     THREAD_PRIORITY_HIGHEST) == 0) {
		return;
	}

	SetThreadPriorityBoost(hThreadHandle,
	                       TRUE);
	SetThreadAffinityMask(hThreadHandle,
	                      0x01);

	/*
	 * Start the thread that simulates the timer peripheral to generate tick interrupts.
	 */
	OSTick_Thread = CreateThread(NULL,
	                             0,
	                             ThreadforSysTickTimer,
	                             0,
	                             CREATE_SUSPENDED,
	                             &OSTick_ThreadID);

	if(OSTick_Thread == NULL) {
		//Display Error Message


		return;
	}

	SetThreadPriority(OSTick_Thread,
	                  THREAD_PRIORITY_NORMAL);
	SetThreadPriorityBoost(OSTick_Thread,
	                       TRUE);
	SetThreadAffinityMask(OSTick_Thread,
	                      0x01);

	/*
	 * Set timer Caps
	 */
	if(timeGetDevCaps(&OSTick_TimerCap,
	                  sizeof(OSTick_TimerCap)) != TIMERR_NOERROR) {

		CloseHandle(OSTick_Thread);

		return;
	}

	if(OSTick_TimerCap.wPeriodMin < WIN_WM_MIN_RES) {
		OSTick_TimerCap.wPeriodMin = WIN_WM_MIN_RES;
	}

	if(timeBeginPeriod(OSTick_TimerCap.wPeriodMin) != TIMERR_NOERROR) {
		CloseHandle(OSTick_Thread);

		return;
	}

	OSTick_SignalPtr = CreateEvent(NULL, TRUE, FALSE, NULL);

	if(OSTick_SignalPtr == NULL) {
		// disp error message

		timeEndPeriod(OSTick_TimerCap.wPeriodMin);
		CloseHandle(OSTick_Thread);

		return;
	}

	OSTick_TimerID = timeSetEvent((UINT)(1000 / RT_TICK_PER_SECOND),
	                              (UINT)   OSTick_TimerCap.wPeriodMin,
	                              (LPTIMECALLBACK)   OSTick_SignalPtr,
	                              (DWORD_PTR)   NULL,
	                              (UINT)(TIME_PERIODIC | TIME_CALLBACK_EVENT_SET));

	if(OSTick_TimerID == 0) {
		//disp

		CloseHandle(OSTick_SignalPtr);
		timeEndPeriod(OSTick_TimerCap.wPeriodMin);
		CloseHandle(OSTick_Thread);

		return;
	}

	/*
	 * Start OS Tick Thread an release Interrupt Mutex
	 */
	ResumeThread(OSTick_Thread);
	ReleaseMutex(hInterruptEventMutex);

	//trigger YEILD INTERRUPT
	TriggerSimulateInterrupt(CPU_INTERRUPT_YIELD);

	/*
	 * block on the mutex that ensure exclusive access to the simulated interrupt objects
	 *  and the events that signals that a simulated interrupt should be processed.
	 */

	hInterruptObjectList[0] = hInterruptEventHandle;
	hInterruptObjectList[1] = hInterruptEventMutex;


	while(1) {
		WaitForMultipleObjects(sizeof(hInterruptObjectList) / sizeof(HANDLE),
		                       hInterruptObjectList,
		                       TRUE,
		                       INFINITE);

		/*
		 * Used to indicate whether the simulate interrupt processing has necessitated a contex
		 * switch to another thread
		 */
		SwitchRequiredMask = 0;

		/*
		 * For each interrupt we are interested in processing ,each of which is represented
		 * by a bit in the 32bit CpuPendingInterrupts variable.
		 */
		for(i = 0; i < MAX_INTERRUPT_NUM; ++i) {
			/* is the simulated interrupt pending ? */
			if(CpuPendingInterrupts & (1UL << i)) {
				/* Is a handler installed ?*/
				if(CpuIsrHandler[i] != NULL) {
					/* Run the actual handler */
					if(CpuIsrHandler[i]() != 0) {
						SwitchRequiredMask |= (1UL << i);
					}
				}

				/* Clear the interrupt pending bit */
				CpuPendingInterrupts &= ~(1UL << i);
			}
		}

		if(SwitchRequiredMask != 0) {
			WinThreadFrom = (win_thread_t*)rt_interrupt_from_thread;
			WinThreadTo = (win_thread_t*)rt_interrupt_to_thread;

			if((WinThreadFrom != NULL) && (WinThreadFrom->ThreadHandle != NULL)) {
				SuspendThread(WinThreadFrom->ThreadHandle);
			}

			ResumeThread(WinThreadTo->ThreadHandle);

		}

		ReleaseMutex(hInterruptEventMutex);
	}
} /*** WinThreadScheduler ***/



/*
*********************************************************************************************************
*                                            ThreadforSysTickTimer()
* Description : win thread to simulate a systick timer
* Argument(s) : LPVOID lpParam
* Return(s)   : static DWORD WINAPI
* Caller(s)   : none
* Note(s)     : This is not a real time way of generating tick events as the next wake time should be relative
*               to the previous wake time,not the time Sleep() is called.
*               It is done this way to prevent overruns in this very non real time simulated/emulated environment
*********************************************************************************************************
*/
static DWORD WINAPI ThreadforSysTickTimer(LPVOID lpParam)
{

	(void)lpParam;              //prevent compiler warnings

	for(;;) {
		/*
		 * Wait until the timer expires and we can access the simulated interrupt variables.
		 */
		WaitForSingleObject(OSTick_SignalPtr, INFINITE);

		ResetEvent(OSTick_SignalPtr);

		/*
		 * Trigger a systick interrupt
		 */
		TriggerSimulateInterrupt(CPU_INTERRUPT_TICK);

	}

	return 0;

} /*** prvThreadforSysTickTimer ***/

/*
*********************************************************************************************************
*                                            SysTickInterruptHandle()
* Description : Interrupt handle for systick
* Argument(s) : void
* Return(s)   : rt_uint32_t
* Caller(s)   : none
* Note(s)     : none
*********************************************************************************************************
*/
rt_uint32_t SysTickInterruptHandle(void)
{

	/* enter interrupt */
	rt_interrupt_enter();

	rt_tick_increase();

	/* leave interrupt */
	rt_interrupt_leave();

	return 0;
} /*** SysTickInterruptHandle ***/

/*
*********************************************************************************************************
*                                            YieldInterruptHandle()
* Description : Interrupt handle for Yield
* Argument(s) : void
* Return(s)   : rt_uint32_t
* Caller(s)   : none
* Note(s)     : none
*********************************************************************************************************
*/
rt_uint32_t YieldInterruptHandle(void)
{

	/*
	 * if rt_thread_switch_interrupt_flag = 1 yield already handled
	 */
	if(rt_thread_switch_interrupt_flag != 0) {
		rt_thread_switch_interrupt_flag = 0;

		/* return thread switch request = 1 */
		return 1;
	}

	return 0;
} /*** YieldInterruptHandle ***/
